/*
     Copyright 2007 Joseph M. Ferner

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Globalization;
using System.Net;
using log4net;
using OpenServers.Net.FtpServer.State;

namespace OpenServers.Net.FtpServer.Command {
    /// <summary>
    ///     <para>
    ///         <b>DATA PORT (PORT)</b><br/>
    ///         The argument is a HOST-PORT specification for the data port
    ///         to be used in data connection.  There are defaults for both
    ///         the user and server data ports, and under normal
    ///         circumstances this command and its reply are not needed.  If
    ///         this command is used, the argument is the concatenation of a
    ///         32-bit internet host address and a 16-bit TCP port address.
    ///         This address information is broken into 8-bit fields and the
    ///         value of each field is transmitted as a decimal number (in
    ///         character string representation).  The fields are separated
    ///         by commas.  A port command would be:
    /// 
    ///         PORT h1,h2,h3,h4,p1,p2
    /// 
    ///         where h1 is the high order 8 bits of the internet host
    ///         address.
    ///     </para>
    ///     <para>
    ///         <b>Format</b><br/>
    ///         <code>
    /// PORT &lt;SP&gt; &lt;host-port&gt; &lt;CRLF&gt;<br/>
    /// &lt;host-port&gt; ::= &lt;host-number&gt;,&lt;port-number&gt;<br/>
    /// &lt;host-number&gt; ::= &lt;number&gt;,&lt;number&gt;,&lt;number&gt;,&lt;number&gt;<br/>
    /// &lt;port-number&gt; ::= &lt;number&gt;,&lt;number&gt;<br/></code>
    ///     </para>
    ///     <para>
    ///         <b>Success:</b>
    ///         <list type="bullet">
    ///             <item><term>200</term><description>Command okay.</description></item>
    ///         </list>
    ///     </para>
    ///     <para>
    ///         <b>Errors:</b>
    ///         <list type="bullet">
    ///             <item><term>421</term><description>Service not available, closing control connection.</description></item>
    ///             <item><term>500</term><description>Syntax error, command unrecognized.</description></item>
    ///             <item><term>501</term><description>Syntax error in parameters or arguments.</description></item>
    ///             <item><term>530</term><description>Not logged in.</description></item>
    ///         </list>
    ///     </para>
    /// </summary>
    public class PortCommand : BaseCommand {
        private static readonly ILog log = LogManager.GetLogger(typeof(PortCommand));

        /// <summary>
        /// Sets the IP address and port to send/receive the next set of data.
        /// </summary>
        public override void PerformCommand(IFtpServer server, FtpState state, string arguments) {
            state.LastPortCommandIpAddress = null;
            state.LastPortCommandPort = null;
            string[] args = arguments.Split(',');
            if (args.Length != 6) {
                log.WarnFormat("invalid arguments for PORT command [{0}]", arguments);
                state.Listener.Send(state, server.Messages.Get501());
            } else {
                try {
                    int ip0 = int.Parse(args[0]);
                    int ip1 = int.Parse(args[1]);
                    int ip2 = int.Parse(args[2]);
                    int ip3 = int.Parse(args[3]);
                    int port0 = int.Parse(args[4]);
                    int port1 = int.Parse(args[5]);
                    IPAddress ipAddress = IPAddress.Parse(string.Format("{0}.{1}.{2}.{3}", ip0, ip1, ip2, ip3));
                    string portHexString = string.Format("{0}{1}", port0.ToString("X2"), port1.ToString("X2"));
                    int port = int.Parse(portHexString, NumberStyles.HexNumber);

                    state.LastPortCommandIpAddress = ipAddress;
                    state.LastPortCommandPort = port;

                    state.IsPassive = false;
                    state.Listener.Send(state, server.Messages.Get200());
                } catch (Exception ex) {
                    log.Warn(string.Format("invalid arguments for PORT command [{0}]", arguments), ex);
                    state.Listener.Send(state, server.Messages.Get501());
                }
            }
        }
    }
}
